#include "iec101Impl.h"
#include "iec101transfer.h"
#include "IEC_asduDecoder.h"
#include "IEC101_encoder.h"

#include "phylink.h"
#include "tools/auto_guard.h"
#include "tools/m_sleep.h"


using namespace::XIEC101;


enum IEC101_RET {
    IEC_LINK_LOST = -1,
    IEC_REQUEST_LINK_STATUS,
    IEC_RESET_LINK,
    IEC_GEN,
    IEC_REQUEST_PRIMARY_DATA,
    IEC_REQUEST_SENCODLY_DATA,
    IEC_CONTINUE_REQUEST_PRIMARY_DATA,
    IEC_CONTINUE_REQUEST_SENCODLY_DATA
};


class IEC101Private {
public:
    IEC101Private(IEC101Impl *impl, IPhyLink *phy_link, uint16_t slave_id);
    ~IEC101Private();
    void reversalFCB(void) { this->fcb_ = !this->fcb_; }
    int requestLinkStatus(void);
    int resetLink(void);
    int primaryData(void);
    int secondaryData(void);
    int gen(void);
    bool checkLinkAddr(const uint8_t * buf, uint16_t slave_id);
    void parseData(uint8_t *asdu_data, uint8_t len);

    IEC101Impl *impl_;
    IPhyLink *com_;
    uint16_t slave_id_;
    bool fcb_;
    bool gen_all_;
    int state_;
    IEC101Impl::LinkState link_state_;
    MMutex mtx_;
    XIEC101::Settings protocol_settings_;
    uint8_t *tx_buf_;
    uint8_t *rx_buf_;
    uint32_t gen_again_time_ms;
};

IEC101Private::IEC101Private(IEC101Impl *impl, IPhyLink *phy_link, uint16_t slave_id) :
    impl_(impl),
    com_(phy_link),
    slave_id_(slave_id),
    fcb_(true),
    gen_all_(false),
    state_(IEC_REQUEST_LINK_STATUS),
    link_state_(IEC101Impl::NOT_LINK),
    tx_buf_(new uint8_t[256]),
    rx_buf_(new uint8_t[256])
{
    const ComSettings *settings = phy_link->comSettings();
    protocol_settings_.iec_settings.coa_len           = settings->iec101.coa_len;
    protocol_settings_.iec_settings.cot_len           = settings->iec101.cot_len;
    protocol_settings_.iec_settings.info_obj_addr_len = settings->iec101.info_obj_addr_len;
    protocol_settings_.link_addr_len                  = settings->iec101.link_addr_len;
}

IEC101Private::~IEC101Private()
{
    delete [] tx_buf_;
    delete [] rx_buf_;
}

int IEC101Private::requestLinkStatus()
{
    uint8_t tx_len = CIEC101Encoder::requestLinkStatus(tx_buf_, slave_id_, &protocol_settings_);
    uint8_t rx_len = 0;
    if (!IEC101Transfer::sendCmdAndRecv(com_, tx_buf_, tx_len, rx_buf_, &rx_len)) {
        return IEC_LINK_LOST;
    }

    if (rx_buf_[0]!=0x10 || !checkLinkAddr(rx_buf_+2, slave_id_))
        return IEC_REQUEST_LINK_STATUS;

    M_CtrlField ctrl;
    ctrl.data = rx_buf_[1];
    switch (ctrl.bits.FC) {
    case FC_ACK_WITH_LINK_STATUS:
        if (ctrl.bits.DFC)
            return IEC_REQUEST_LINK_STATUS;
        else
            return IEC_RESET_LINK;
    case FC_LINK_NO_READY:
    case FC_LINK_NO_RUN:
    default:
        return IEC_REQUEST_LINK_STATUS;
    }
}

int IEC101Private::resetLink()
{
    uint8_t tx_len = CIEC101Encoder::resetLink(tx_buf_, slave_id_, &protocol_settings_);
    uint8_t rx_len = 0;
    if (!IEC101Transfer::sendCmdAndRecv(com_, tx_buf_, tx_len, rx_buf_, &rx_len)) {
        return IEC_LINK_LOST;
    }
    if (rx_buf_[0]!=0x10 || !checkLinkAddr(rx_buf_+2, slave_id_))
        return IEC_RESET_LINK;
    M_CtrlField ctrl;
    ctrl.data = rx_buf_[1];
    switch (ctrl.bits.FC) {
    case FC_ACCEPT_CONFIRM:
        return IEC_GEN;
    case FC_LINK_NO_READY:
    case FC_LINK_NO_RUN:
    case FC_DENY_CONFIRM:
    default:
        return IEC_RESET_LINK;
    }
}

int IEC101Private::primaryData()
{
    uint8_t tx_len = CIEC101Encoder::requestPrimaryData(tx_buf_, fcb_, slave_id_, &protocol_settings_);
    reversalFCB();
    uint8_t rx_len = 0;
    if (!IEC101Transfer::sendCmdAndRecv(com_, tx_buf_, tx_len, rx_buf_, &rx_len)) {
        return IEC_LINK_LOST;
    }
    if (rx_buf_[0] == 0xE5)
        return IEC_REQUEST_SENCODLY_DATA;
    M_CtrlField ctrl;
    if (rx_buf_[0] == 0x10) {
        if (!checkLinkAddr(rx_buf_+2, slave_id_))
            return IEC_REQUEST_LINK_STATUS;
        ctrl.data = rx_buf_[1];
    }
    else if (rx_buf_[0] == 0x68) {
        if (!checkLinkAddr(rx_buf_+5, slave_id_))
            return IEC_REQUEST_LINK_STATUS;
        ctrl.data = rx_buf_[4];
    }
    uint8_t *asdu = CIEC101Encoder::asduPtr(rx_buf_, &protocol_settings_);
    switch (ctrl.bits.FC) {
    case FC_ACK_WITH_DATA:
        parseData(asdu, rx_len-static_cast<uint8_t>(asdu-rx_buf_));
    case FC_ACK_NO_DATA:
        if (ctrl.bits.ACD)
            return IEC_CONTINUE_REQUEST_PRIMARY_DATA;
        else
            return IEC_REQUEST_SENCODLY_DATA;
    default:
        return IEC_REQUEST_LINK_STATUS;
    }
}

int IEC101Private::secondaryData()
{
    uint8_t tx_len = CIEC101Encoder::requestSecondaryData(tx_buf_, fcb_, slave_id_, &protocol_settings_);
    reversalFCB();
    uint8_t rx_len = 0;
    if (!IEC101Transfer::sendCmdAndRecv(com_, tx_buf_, tx_len, rx_buf_, &rx_len)) {
        return IEC_LINK_LOST;
    }
    if (rx_buf_[0] == 0xE5)
        return IEC_REQUEST_SENCODLY_DATA;
    M_CtrlField ctrl;
    if (rx_buf_[0] == 0x10) {
        if (!checkLinkAddr(rx_buf_+2, slave_id_))
            return IEC_REQUEST_LINK_STATUS;
        ctrl.data = rx_buf_[1];
    }
    else if (rx_buf_[0] == 0x68) {
        if (!checkLinkAddr(rx_buf_+5, slave_id_))
            return IEC_REQUEST_LINK_STATUS;
        ctrl.data = rx_buf_[4];
    }
    if (ctrl.bits.ACD)
        return IEC_CONTINUE_REQUEST_PRIMARY_DATA;
    else {
        if (ctrl.bits.FC == FC_ACK_WITH_DATA) {
            uint8_t *asdu = CIEC101Encoder::asduPtr(rx_buf_, &protocol_settings_);
            parseData(asdu, rx_len-static_cast<uint8_t>(asdu-rx_buf_));
            return IEC_CONTINUE_REQUEST_SENCODLY_DATA;
        }
        else
            return IEC_REQUEST_SENCODLY_DATA;
    }
}

int IEC101Private::gen()
{
    uint8_t tx_len = CIEC101Encoder::gen(tx_buf_, this->fcb_, 20, slave_id_, &protocol_settings_);
    reversalFCB();
    uint8_t rx_len = 0;
    if (!IEC101Transfer::sendCmdAndRecv(com_, tx_buf_, tx_len, rx_buf_, &rx_len)) {
        return IEC_LINK_LOST;
    }
    if (rx_buf_[0] != 0x10 || !checkLinkAddr(rx_buf_+2, slave_id_))
        return IEC_GEN;
    M_CtrlField ctrl;
    ctrl.data = rx_buf_[1];
    if (ctrl.bits.FC != FC_ACCEPT_CONFIRM)
        return IEC_GEN;
    if (ctrl.bits.ACD)
        return IEC_CONTINUE_REQUEST_PRIMARY_DATA;
    else
        return IEC_CONTINUE_REQUEST_SENCODLY_DATA;
}

bool IEC101Private::checkLinkAddr(const uint8_t *buf, uint16_t slave_id)
{
    if (buf[0] != (slave_id&0xFF) )
        return false;
    if (protocol_settings_.link_addr_len > 1)
        if (buf[1] != (slave_id>>8))
            return false;
    return true;
}

void IEC101Private::parseData(uint8_t *asdu_data, uint8_t len)
{
    DataUnitTag dataUnit;
    CVectInfoObject objs;

    if (CAsduDecoder::decode(asdu_data, len, dataUnit, objs, protocol_settings_.iec_settings)) {
        switch (dataUnit.typeId) {
        case 1:     //M_SP_NA_1 不带时标的单点信息
        case 2:     //M_SP_TA_1 带时标的单点信息
        case 30:    //M_SP_TB_1 带时标CP56Time2a的单点信息
            for (CVectInfoObject::iterator it=objs.begin(); it!=objs.end(); it++) {
                CInfoObject_SIQ *obj = dynamic_cast<CInfoObject_SIQ *>( *it);
                if (obj) {
                    const SIQ *siq = obj->value();
                    if (siq->valid())
                        impl_->OnSetSIP(static_cast<uint16_t>(obj->addr().d),
                                        static_cast<IEC101Impl::SinglePointStatus>(siq->SPI));
                }
            }
            break;
        case 3:     //M_DP_NA_1 不带时标的双点信息
        case 4:     //M_DP_TA_1 带时标的双点信息
        case 31:    //M_DP_TB_1 带时标CP56Time2a的双点信息
            for (CVectInfoObject::iterator it=objs.begin(); it!=objs.end(); it++) {
                CInfoObject_DIQ *obj = dynamic_cast<CInfoObject_DIQ *>( *it);
                if (obj) {
                    const DIQ *diq = obj->value();
                    if (diq->valid())
                        impl_->OnSetDIP(static_cast<uint16_t>(obj->addr().d),
                                        static_cast<IEC101Impl::DoublePointStatus>(diq->DPI));
                }
            }
            break;
        case 9:     //M_ME_NA_1 测量值, 不带时标的规一化值
        case 10:    //M_ME_TA_1 测量值, 带时标的规一化值
        case 34:    //M_ME_TD_1 测量值, 带时标CP56Time2a的规一化值
        case 21:    //M_ME_ND_1 测量值, 不带品质描述词的规一化值
            for (CVectInfoObject::iterator it=objs.begin(); it!=objs.end(); it++) {
                CInfoObject_NVA *obj = dynamic_cast<CInfoObject_NVA *>(*it);
                if (obj) {
                    impl_->OnSetMeasure(static_cast<uint16_t>(obj->addr().d), obj->value()->value);
                }
            }
            break;
        case 11:    //M_ME_NB_1 测量值, 标度化值
        case 12:    //M_ME_TB_1 测量值, 带时标的标度化值
        case 35:    //M_ME_TE_1 测量值, 带时标CP56Time2a的标度化值
            for (CVectInfoObject::iterator it=objs.begin(); it!=objs.end(); it++) {
                CInfoObject_SVA_QDS *obj = dynamic_cast<CInfoObject_SVA_QDS *>(*it);
                if (obj) {
                    if (obj->qs()->valid())
                        impl_->OnSetMeasure(static_cast<uint16_t>(obj->addr().d), obj->value()->value);
                }
            }
            break;
        case 13:    //M_ME_NC_1 测量值, 短浮点数
        case 14:    //M_ME_TC_1 测量值, 带时标短浮点数
        case 36:    //M_ME_TF_1 测量值, 带时标CP56Time2a的短浮点数
            for (CVectInfoObject::iterator it=objs.begin(); it!=objs.end(); it++) {
                CInfoObject_FLOAT_QDS *obj = dynamic_cast<CInfoObject_FLOAT_QDS *>(*it);
                if (obj) {
                    if (obj->qs()->valid())
                        impl_->OnSetMeasure(static_cast<uint16_t>(obj->addr().d), obj->value()->value);
                }
            }
            break;
        default:
            break;
        }
    }
}


IEC101Impl::IEC101Impl() :
    private_(nullptr)
{

}

IEC101Impl::IEC101Impl(IPhyLink *phy_link, uint16_t slave_id) :
    private_(new IEC101Private(this, phy_link, slave_id))
{
    pthread_t id;
    pthread_create(&id, nullptr, timerProc, this);
}

IEC101Impl::~IEC101Impl()
{
    delete private_;
}

bool IEC101Impl::setDevAndId(IPhyLink *phy_link, uint16_t slave_id)
{
    if (private_)
        return false;
    private_ = new IEC101Private(this, phy_link, slave_id);
    pthread_t id;
    pthread_create(&id, nullptr, timerProc, this);
    return !!private_;
}

bool IEC101Impl::linkDevice()
{
    uint32_t retryCnt=0;
    int state = IEC_REQUEST_LINK_STATUS;
    const ComSettings *settings = private_->com_->comSettings();
    private_->state_  = IEC_REQUEST_LINK_STATUS;
    while ( (state=private_->requestLinkStatus()) != IEC_RESET_LINK ) {
        retryCnt++;
        if (retryCnt > settings->retry_cnt_at_recv_timeout)
            return false;

        mSleep(settings->cmd_interval_ms);
    }
    mSleep(settings->cmd_interval_ms);
    retryCnt = 0;
    while ( (state=private_->resetLink()) != IEC_GEN ) {
        retryCnt++;
        if (retryCnt > settings->retry_cnt_at_recv_timeout)
            return false;
    }
    mSleep(settings->cmd_interval_ms);
    private_->state_ = IEC_GEN;
    private_->link_state_ = LINKED;
    private_->fcb_ = true;
    return true;
}


void IEC101Impl::requestData()
{
    bool exit = false;
    uint32_t retryCnt = 0;
    int pre_state = private_->state_;
    const ComSettings *settings = private_->com_->comSettings();

    AUTO_GUARD(gd, MMutex, private_->mtx_);
    if (private_->gen_all_)
    {
        private_->state_ = IEC_GEN;
        private_->gen_all_ = false;
    }

    while(true)
    {
        pre_state = private_->state_;
        switch (private_->state_)
        {
        case IEC_REQUEST_PRIMARY_DATA:
        case IEC_CONTINUE_REQUEST_PRIMARY_DATA:
            private_->state_ = private_->primaryData();
            break;
        case IEC_REQUEST_SENCODLY_DATA:
        case IEC_CONTINUE_REQUEST_SENCODLY_DATA:
            private_->state_ = private_->secondaryData();
            break;
        case IEC_GEN:
            private_->state_ = private_->gen();
            break;
        default:
            private_->state_ = IEC_LINK_LOST;
            break;
        }
        if (private_->state_ < IEC_REQUEST_PRIMARY_DATA)
        {
            private_->state_ = pre_state;
            retryCnt++;
            if (retryCnt >= settings->retry_cnt_at_recv_timeout)
            {
                private_->link_state_ = LINK_LOST;
                private_->state_ = IEC_REQUEST_LINK_STATUS;
                OnLinkLost();
                exit = true;
            }
        }
        else
        {
            if (private_->state_ != IEC_CONTINUE_REQUEST_PRIMARY_DATA &&
                private_->state_ != IEC_CONTINUE_REQUEST_SENCODLY_DATA)
            {
                exit = true;
            }
        }
        if (exit)
            break;
        else
            mSleep(settings->cmd_interval_ms);
    }
}

void IEC101Impl::setGenAll()
{
    AUTO_GUARD(gd, MMutex, private_->mtx_);
    private_->state_ = IEC_GEN;
}

bool IEC101Impl::linked() const
{
    return private_->link_state_ == LINKED;
}

void *IEC101Impl::timerProc(void *argv)
{
    IEC101Impl * p_this = reinterpret_cast<IEC101Impl *>(argv);
    uint32_t gen_time_ms = 900000;
    while (true) {
        mSleep(gen_time_ms);
        if (p_this->linked()) {
            p_this->private_->gen_all_ = true;
        }
    }
}
